#include "Timer.h"

/* 时钟原子 */
static struct _ClockAtomic
{
        TIM_TypeDef *tim;
        Timer       *timer;
} tim[3] = {
        {TIM2, NULL},
        {TIM3, NULL},
        {TIM4, NULL},
};

static bool clock_atomic_inited = false;

/**
 * @brief 设置时钟原子的 PSC ARR
 */
static void
clock_atomic_set_psc_arr(ClockAtomic *tim_atomic, uint16_t psc, uint16_t arr)
{
        static TIM_TimeBaseInitTypeDef tim_fixed = {
                .TIM_ClockDivision     = TIM_CKD_DIV1,
                .TIM_CounterMode       = TIM_CounterMode_Up,
                .TIM_RepetitionCounter = 0,
                .TIM_Prescaler         = 7200,
                .TIM_Period            = 5000};
        tim_fixed.TIM_Period    = arr - 1;
        tim_fixed.TIM_Prescaler = psc - 1;
        TIM_TimeBaseInit(tim_atomic->tim, &tim_fixed);
}

static void
clock_atomic_init(void)
{
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 | RCC_APB1Periph_TIM3 |
                                       RCC_APB1Periph_TIM4,
                               ENABLE);
        TIM_InternalClockConfig(tim[0].tim);
        TIM_InternalClockConfig(tim[1].tim);
        TIM_InternalClockConfig(tim[2].tim);

        clock_atomic_set_psc_arr(&tim[0], 7200, 1000);
        clock_atomic_set_psc_arr(&tim[1], 7200, 1000);
        clock_atomic_set_psc_arr(&tim[2], 7200, 1000);

        TIM_ClearFlag(tim[0].tim, TIM_FLAG_Update);
        TIM_ClearFlag(tim[1].tim, TIM_FLAG_Update);
        TIM_ClearFlag(tim[2].tim, TIM_FLAG_Update);

        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

        NVIC_InitTypeDef nvic = {.NVIC_IRQChannelCmd                = ENABLE,
                                 .NVIC_IRQChannelPreemptionPriority = 2,
                                 .NVIC_IRQChannelSubPriority        = 1,
                                 .NVIC_IRQChannel = TIM2_IRQn};
        NVIC_Init(&nvic);
        nvic.NVIC_IRQChannel = TIM3_IRQn;
        NVIC_Init(&nvic);
        nvic.NVIC_IRQChannel = TIM4_IRQn;
        NVIC_Init(&nvic);
}

/**
 * @brief 获取一个空闲的时钟原子，请注意，取得的时钟原子必须连接到 Timer 对象
 */
static ClockAtomic *
clock_atomic_get_idle(void)
{
        for (short i = 0; i < 3; ++i)
                if (!tim[i].timer)
                        return &tim[i];
        return NULL;
}

/**
 * @brief 释放时钟原子
 */
static void
clock_atomic_release(ClockAtomic *tim_atomic)
{
        tim_atomic->timer = NULL;
}

/**
 * @brief 连接对象和钩子到时钟原子
 */
static void
clock_atomic_connect(ClockAtomic *tim_atomic, Timer *timer)
{
        tim_atomic->timer = timer;
}

void
timer_init(Timer *self,
           const ObjectIRQHandler handler,
           const uint16_t psc,
           const uint16_t arr)
{
        if (!clock_atomic_inited) {
                clock_atomic_init();
                clock_atomic_inited = true;
        }
        self->tim_atomic = NULL;
        self->psc        = psc;
        self->arr        = arr;
        self->handler_cb = handler;
        self->is_started = false;
}

void
timer_start(Timer *self)
{
        self->tim_atomic = clock_atomic_get_idle();
        if (!self->tim_atomic)
                return;
        clock_atomic_set_psc_arr(self->tim_atomic, self->psc, self->arr);
        clock_atomic_connect(self->tim_atomic, self);
        self->is_started = true;
        TIM_Cmd(self->tim_atomic->tim, ENABLE);
        TIM_ITConfig(self->tim_atomic->tim, TIM_IT_Update, ENABLE);
}

void
timer_stop(Timer *self)
{
        if (!self->is_started)
                return;
        self->is_started = false;
        TIM_Cmd(self->tim_atomic->tim, DISABLE);
        TIM_ITConfig(self->tim_atomic->tim, TIM_IT_Update, DISABLE);
        TIM_ClearITPendingBit(self->tim_atomic->tim, TIM_IT_Update);
        clock_atomic_release(self->tim_atomic);
}

void
timer_set_psc_arr(Timer *self, const uint16_t psc, const uint16_t arr)
{
        bool will_restore = false;
        if (self->is_started) {
                timer_stop(self);
                will_restore = true;
        }
        self->psc = psc;
        self->arr = arr;
        if (will_restore)
                timer_start(self);
}

void
TIM2_IRQHandler(void)
{
        if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) {
                if (tim[0].timer && tim[0].timer->handler_cb)
                        tim[0].timer->handler_cb(tim[0].timer);
                TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
        }
}

void
TIM3_IRQHandler(void)
{
        if (TIM_GetITStatus(TIM3, TIM_IT_Update) == SET) {
                if (tim[1].timer && tim[1].timer->handler_cb)
                        tim[1].timer->handler_cb(tim[1].timer);
                TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
        }
}

void
TIM4_IRQHandler(void)
{
        if (TIM_GetITStatus(TIM4, TIM_IT_Update) == SET) {
                if (tim[2].timer && tim[2].timer->handler_cb)
                        tim[2].timer->handler_cb(tim[2].timer);
                TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
        }
}
